﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace System
{
    /// <summary>
    /// 字节扩展
    /// </summary>
    public static class ByteEx
    {
        /// <summary>
        /// 序列化
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static byte[] ObjectSerialize(this object data)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                new BinaryFormatter().Serialize(stream, data);
                byte[] newArray = new byte[stream.Length];
                stream.Position = 0;
                stream.Read(newArray, 0, (int)stream.Length);
                stream.Close();
                return newArray;
            }
        }
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static object ObjectDeserialize(this byte[] bytes)
        {
            using (MemoryStream serializationStream = new MemoryStream(bytes))
            {
                return new BinaryFormatter().Deserialize(serializationStream);
            }
        }
        /// <summary>
        /// 显示hex，不带0x
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string ToHex(this byte[] bytes, int index = 0, int count = -1)
        {
            _ = bytes.Length;
            StringBuilder stringBuilder = new StringBuilder();
            while (index < bytes.Length)
            {
                byte b = bytes[index++];
                stringBuilder.Append(GetHexValue((int)b / 16));
                stringBuilder.Append(GetHexValue((int)b % 16));
                if (count != -1 && stringBuilder.Length == count * 2)
                {
                    break;
                }
            }

            return stringBuilder.ToString();
        }
        /// <summary>
        /// 获取hex字符
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        private static char GetHexValue(int i)
        {
            if (i < 10)
            {
                return (char)(i + 48);
            }

            return (char)(i - 10 + 65);
        }
        /// <summary>
        /// 显示带0x的hex
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string ToHexX(this byte[] bytes, int index = 0, int count = -1)
        {
            int sind = index;
            StringBuilder sb = new StringBuilder(100);
            sb.Append("{");
            if (bytes != null && bytes.Length > 0)
            {
                while (index < bytes.Length)
                {
                    byte b = bytes[index];
                    sb.AppendFormat("0x{0:X2},", b);
                    if (count != -1 && (index - sind + 1) >= count)
                    {
                        break;
                    }
                    index++;
                }
            }
            if (sb.Length > 1)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            sb.Append("}");
            return sb.ToString();
        }
        /// <summary>
        /// 转换流
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static MemoryStream ToStream(this byte[] value)
        {
            return new MemoryStream(value);
        }
        /// <summary>
        /// 获取字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string GetString(this byte[] bytes, Encoding encoding = null)
        {
            return (encoding ?? Encoding.UTF8).GetString(bytes);
        }
        /// <summary>
        /// 获取字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string GetString(this byte[] bytes, int index, int count, Encoding encoding = null)
        {
            return (encoding ?? Encoding.UTF8).GetString(bytes, index, count);
        }
        /// <summary>
        /// 转换base64
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string ToBase64String(this byte[] value)
        {
            return Convert.ToBase64String(value);
        }
        /// <summary>
        /// 转int
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static int ToInt32(this byte[] bytes, int index = 0)
        {
            if (bytes.Length < index + 4) { throw new ArgumentException("bytes too short"); }
            return (bytes[index] << 24) | (bytes[index + 1] << 16) | (bytes[index + 2] << 8) | bytes[index + 3];
        }
        /// <summary>
        /// 转uint
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static uint ToUInt32(this byte[] bytes, int index = 0)
        {
            if (bytes.Length < index + 4) { throw new ArgumentException("bytes too short"); }
            return (uint)((bytes[index] << 24) | (bytes[index + 1] << 16) | (bytes[index + 2] << 8) | bytes[index + 3]);
        }
        /// <summary>
        /// 转long
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static long ToInt64(this byte[] bytes, int index = 0)
        {
            if (bytes.Length < index + 8) { throw new ArgumentException("bytes too short"); }
            return (long)(((ulong)(bytes[index]) << 56) | ((ulong)bytes[index + 1] << 48) | ((ulong)bytes[index + 2] << 40) | ((ulong)bytes[index + 3] << 32) | ((ulong)bytes[index + 4] << 24)
                | ((ulong)bytes[index + 5] << 16) | ((ulong)bytes[index + 6] << 8) | bytes[index + 7]);

        }
        /// <summary>
        /// 转ulong
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static ulong ToUInt64(this byte[] bytes, int index = 0)
        {
            if (bytes.Length < index + 8) { throw new ArgumentException("bytes too short"); }
            return (ulong)(((ulong)(bytes[index]) << 56) | ((ulong)bytes[index + 1] << 48) | ((ulong)bytes[index + 2] << 40) | ((ulong)bytes[index + 3] << 32) | ((ulong)bytes[index + 4] << 24)
                | ((ulong)bytes[index + 5] << 16) | ((ulong)bytes[index + 6] << 8) | bytes[index + 7]);
        }
        /// <summary>
        /// 转布尔
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static bool ToBoolean(this byte[] bytes, int index = 0)
        {
            return bytes[index] != 0;
        }
        /// <summary>
        /// 转换双精度
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static double ToDouble(this byte[] bytes, int startIndex = 0)
        {
            byte[] _datas = new byte[bytes.Length - startIndex];
            Array.Copy(bytes, startIndex, _datas, 0, _datas.Length);
            Array.Reverse(_datas);
            return BitConverter.ToDouble(_datas, 0);
        }
        /// <summary>
        /// 转换单精度
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static float ToFloat(this byte[] bytes, int startIndex = 0)
        {
            byte[] _datas = new byte[bytes.Length - startIndex];
            Array.Copy(bytes, startIndex, _datas, 0, _datas.Length);
            Array.Reverse(_datas);
            return BitConverter.ToSingle(_datas, 0);
        }
        /// <summary>
        /// 转short
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static short ToInt16(this byte[] bytes, int startIndex = 0)
        {
            return (short)((bytes[startIndex] << 8) | bytes[startIndex + 1]);
        }
        /// <summary>
        /// 转ushort
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="startIndex"></param>
        /// <returns></returns>
        public static ushort ToUInt16(this byte[] bytes, int startIndex = 0)
        {
            return (ushort)((bytes[startIndex] << 8) | bytes[startIndex + 1]);
        }
        /// <summary>
        /// 双精度转byte
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this double value)
        {
            var bytes = BitConverter.GetBytes(value);
            Array.Reverse(bytes);
            return bytes;
        }
        /// <summary>
        /// 单精度转byte
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this float value)
        {
            var bytes = BitConverter.GetBytes(value);
            Array.Reverse(bytes);
            return bytes;
        }

        /// <summary>
        /// long转byte
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this long value)
        {
            byte[] array = new byte[8];
            array[7] = (byte)(value & 0xFF);
            array[6] = (byte)((value >> 8) & 0xFF);
            array[5] = (byte)((value >> 16) & 0xFF);
            array[4] = (byte)((value >> 24) & 0xFF);
            array[3] = (byte)((value >> 32) & 0xFF);
            array[2] = (byte)((value >> 40) & 0xFF);
            array[1] = (byte)((value >> 48) & 0xFF);
            array[0] = (byte)((value >> 56) & 0xFF);
            return array;
        }
        /// <summary>
        /// uint64转byte
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this ulong value)
        {
            byte[] array = new byte[8];
            array[7] = (byte)(value & 0xFF);
            array[6] = (byte)((value >> 8) & 0xFF);
            array[5] = (byte)((value >> 16) & 0xFF);
            array[4] = (byte)((value >> 24) & 0xFF);
            array[3] = (byte)((value >> 32) & 0xFF);
            array[2] = (byte)((value >> 40) & 0xFF);
            array[1] = (byte)((value >> 48) & 0xFF);
            array[0] = (byte)((value >> 56) & 0xFF);
            return array;
        }
        /// <summary>
        /// int转byte
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this int i)
        {
            byte[] array = new byte[4];
            array[3] = (byte)((uint)i & 0xFFu);
            array[2] = (byte)((uint)(i >> 8) & 0xFFu);
            array[1] = (byte)((uint)(i >> 16) & 0xFFu);
            array[0] = (byte)((uint)(i >> 24) & 0xFFu);
            return array;
        }
        /// <summary>
        /// unit转byte
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this uint i)
        {
            byte[] array = new byte[4];
            array[3] = (byte)(i & 0xFFu);
            array[2] = (byte)((i >> 8) & 0xFFu);
            array[1] = (byte)((i >> 16) & 0xFFu);
            array[0] = (byte)((i >> 24) & 0xFFu);
            return array;
        }
        /// <summary>
        /// int16转byte
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this short i)
        {
            byte[] array = new byte[2];
            array[1] = (byte)((uint)i & 0xFFu);
            array[0] = (byte)((uint)(i >> 8) & 0xFFu);
            return array;
        }
        /// <summary>
        /// int16转byte
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static byte[] ToBytes(this ushort i)
        {
            byte[] array = new byte[2];
            array[1] = (byte)(i & 0xFFu);
            array[0] = (byte)((uint)(i >> 8) & 0xFFu);
            return array;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="n"></param>
        /// <param name="isUpper"></param>
        /// <returns></returns>
        public static char IntToHex(int n, bool isUpper = false)
        {
            if (isUpper)
            {
                if (n > 9)
                {
                    return (char)(n - 10 + 65);
                }

                return (char)(n + 48);
            }

            if (n > 9)
            {
                return (char)(n - 10 + 97);
            }

            return (char)(n + 48);
        }
        /// <summary>
        /// CRC16
        /// </summary>
        /// <param name="byteData"></param>
        /// <returns></returns>
        public static ushort ToCRC16(this byte[] byteData)
        {
            byte[] array = new byte[2];
            ushort num = ushort.MaxValue;
            for (int i = 0; i < byteData.Length; i++)
            {
                num = (ushort)(num ^ byteData[i]);
                for (int j = 0; j < 8; j++)
                {
                    if ((num & 1) == 1)
                    {
                        num = (ushort)(num >> 1);
                        num = (ushort)(num ^ 0xA001u);
                    }
                    else
                    {
                        num = (ushort)(num >> 1);
                    }
                }
            }

            array[1] = (byte)(num >> 8);
            array[0] = (byte)(num & 0xFFu);
            return array.ToUInt16();
        }
    }
}
